#ifndef _TEST_SERVER_
#define _TEST_SERVER_

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/noncopyable.hpp>
#include <algorithm>
#include <iostream>
#include <map>
#include <string>

using namespace boost::asio;

typedef boost::shared_ptr<ip::tcp::socket> socket_ptr;

struct msg
{
};

class Connect
{
public:
    Connect() = default;
    Connect(socket_ptr sock_ptr, int id) : sock_ptr_(sock_ptr), id_(id) {}
    ~Connect() = default;

private:
    enum
    {
        buffer_size = 1024
    };
    int id_;

public:
    socket_ptr sock_ptr_;
    char read_buf[buffer_size];
    boost::mutex mutex;

public:
    int getId()
    {
        return id_;
    }
    void close()
    {
        sock_ptr_->close();
    }
};

class TestServer : public boost::enable_shared_from_this<TestServer>, boost::noncopyable
{
public:
    TestServer() = default;
    TestServer(io_context &context, int port) : context_(context), acceptor(context, ip::tcp::endpoint(ip::address_v4::any(), port)) {}
    ~TestServer() = default;

private:
    boost::mutex mutex;
    io_context &context_;
    ip::tcp::acceptor acceptor;
    std::map<int, boost::shared_ptr<Connect>> connects;
    boost::thread_group threads;
    int read_thread_count = 5;

public:
    void runWorkThread()
    {
        context_.run();
    }
    void start()
    {
        do_accept();
        for (int i = 0; i < read_thread_count; i++)
        {
            threads.create_thread(boost::bind(&TestServer::runWorkThread, shared_from_this()));
            boost::this_thread::sleep(boost::posix_time::millisec(100));
        }

        threads.join_all();
    }
    void do_accept()
    {
        socket_ptr sock(new ip::tcp::socket(context_));
        acceptor.async_accept(*sock, boost::bind(&TestServer::acceptHandler, shared_from_this(), sock, _1));
    }
    void acceptHandler(socket_ptr sock, const boost::system::error_code &err)
    {
        if (err)
        {
            std::cout << "handle_accept error::msg ---->" << err.message() << " error::code ---->" << err.value() << std::endl;

            return;
        }

        std::cout << "handler_accept >> thread is " << boost::this_thread::get_id() << std::endl;

        // 加锁,添加到已连接客户端列表中
        boost::unique_lock<boost::mutex> lock(mutex);
        boost::shared_ptr<Connect> connect(new Connect(sock, connects.size() + 1));
        connects.insert(std::make_pair(connect->getId(), connect));

        // 开始尝试读取数据
        do_read(connect);
        // 继续接受其他连接请求
        do_accept();
    }
    void do_read(boost::shared_ptr<Connect> connect)
    {
        connect->sock_ptr_->async_read_some(buffer(connect->read_buf), boost::bind(&TestServer::readHandler, shared_from_this(), connect, _1, _2));
    }
    void readHandler(boost::shared_ptr<Connect> connect, const boost::system::error_code &err, std::size_t bytes)
    {
        if (err)
        {
            std::cout << "server read err, " << err.message() << " , connect_id = " << connect->getId() << std::endl;
            connect->close();
            // 移除客户端
            connects.erase(connect->getId());
            return;
        }
        std::cout << "server read bytes >>>" << bytes << std::endl;
        std::cout << "server recv msg --->" << std::string(connect->read_buf, 0, bytes) << "  thread is " << boost::this_thread::get_id() << std::endl;

        // msg process
        if (std::string(connect->read_buf, 0, bytes) == "ping...")
        {
            // char reply[] = "ping...";
            std::stringstream ss;
            ss << "ok---->id--->" << connect->getId();
            std::string str = ss.str();
            const char *reply = str.c_str();
            do_write(connect->getId(), reply, str.length());
        }

        do_read(connect);
    }
    void do_write(int connect_id, const char *data, std::size_t size)
    {
        auto it = connects.find(connect_id);
        if (it != connects.end())
        {
            std::cout << "connect_id = " << connect_id << ",write data = " << data << std::endl;
            async_write(*(it->second->sock_ptr_), buffer(data, size), boost::bind(&TestServer::writeHandler, shared_from_this(), it->second, _1, _2));
        }
    }
    void writeHandler(boost::shared_ptr<Connect> conn, const boost::system::error_code &err, std::size_t bytes)
    {
        if (err)
        {
            std::cerr << "write error client_id = " << conn->getId() << std::endl;
            return;
        }
        std::cout << "write success client_id = " << conn->getId() << " ,write bytes = " << bytes << std::endl;
    }
    void close()
    {
        context_.stop();
        if (acceptor.is_open())
        {
            acceptor.close();
        }
    }
};

#endif